package htmlsoup

import (
	"bytes"
	"encoding/hex"
	"regexp"
	"strings"

	"code.google.com/awwwe-browser/data"
)

type entityReferenceDatum struct {
	Bytes       []byte
	HexString   string
	Speech      string
	Description string
}

func buildEntityReferenceDatum(hexstring string, speech string, description string) entityReferenceDatum {
	var v entityReferenceDatum
	v.HexString = strings.ToLower(hexstring)
	bb, err := hex.DecodeString(hexstring)
	if err == nil {
		v.Bytes = bb
		if len(speech) > 0 {
			v.Speech = speech
		} else {
			bfr := bytes.NewBuffer(bb)
			v.Speech = bfr.String()
		}
		v.Description = description
	} else {
		v.Bytes = data.EmptyBytes
		v.Speech = data.EmptyString
		v.Description = data.EmptyString
	}
	return v
}

type entityReferenceData struct {
	int2key map[int]string
	data    map[string]entityReferenceDatum
}

func (this *entityReferenceData) Set(name string, value entityReferenceDatum) {
	var ikey int
    var ok bool
	this.data[name] = value
    ikey, ok = data.HexStringToInt(value.HexString)
    if ok {
		this.int2key[ikey] = name
    }
}

var entityReferences entityReferenceData
var alphaEntityRegex *regexp.Regexp
var decEntityRegex *regexp.Regexp

func init() {
	// regular expressions
	alphaEntityRegex = regexp.MustCompile(`&[[:alpha:]]+;`)
	decEntityRegex = regexp.MustCompile(`&#[[:digit:]]+;`)
	// references
	entityReferences.data = make(map[string]entityReferenceDatum)
	entityReferences.int2key = make(map[int]string)
	// init and add elements
	entityReferences.Set("gt", buildEntityReferenceDatum("003E", " is greater than ", "greater then sign"))
	entityReferences.Set("lt", buildEntityReferenceDatum("003C", " is less than ", "non breaking space"))
	entityReferences.Set("amp", buildEntityReferenceDatum("0026", " and ", "ampersand"))

	entityReferences.Set("nbsp", buildEntityReferenceDatum("00A0", " ", "non breaking space"))
	entityReferences.Set("iexcl", buildEntityReferenceDatum("00A1", data.EmptyString, "inverted exclamation mark"))
	entityReferences.Set("cent", buildEntityReferenceDatum("00A2", " cents ", "cent sign"))
	entityReferences.Set("pound", buildEntityReferenceDatum("00A3", " pounds ", "pound sign"))
	entityReferences.Set("curren", buildEntityReferenceDatum("00A4", " currency ", "currency sign"))
	entityReferences.Set("yen", buildEntityReferenceDatum("00A5", " yen ", "yen sign = yuan sign"))
	entityReferences.Set("brvbar", buildEntityReferenceDatum("00A6", " broken vertical bar ", "broken bar = broken vertical bar"))
	entityReferences.Set("sect", buildEntityReferenceDatum("00A7", " section ", "section sign"))
	entityReferences.Set("uml", buildEntityReferenceDatum("00A8", data.EmptyString, "diaeresis = spacing diaeresis"))
	entityReferences.Set("copy", buildEntityReferenceDatum("00A9", " copy right ", "copyright sign"))
	entityReferences.Set("ordf", buildEntityReferenceDatum("00AA", data.EmptyString, "feminine ordinal indicator"))
	entityReferences.Set("laquo", buildEntityReferenceDatum("00AB", " quote, ", "left-pointing double angle quotation mark = left pointing guillemet"))
	entityReferences.Set("not", buildEntityReferenceDatum("00AC", " not ", "not sign"))
	entityReferences.Set("shy", buildEntityReferenceDatum("00AD", " ", "soft hyphen = discretionary hyphen"))
	entityReferences.Set("reg", buildEntityReferenceDatum("00AE", " registered trade mark ", "registered sign = registered trade mark sign"))
	entityReferences.Set("macr", buildEntityReferenceDatum("00AF", data.EmptyString, "macron = spacing macron = overline = APL overbar"))
	entityReferences.Set("deg", buildEntityReferenceDatum("00B0", " degrees ", "degree sign"))
	entityReferences.Set("plusmn", buildEntityReferenceDatum("00B1", " plus or minus ", "plus-minus sign = plus-or-minus sign"))
	entityReferences.Set("sup2", buildEntityReferenceDatum("00B2", " squared ", "superscript two = superscript digit two = squared"))
	entityReferences.Set("sup3", buildEntityReferenceDatum("00B3", " cubed ", "superscript three = superscript digit three = cubed"))
	entityReferences.Set("acute", buildEntityReferenceDatum("00B4", data.EmptyString, "acute accent = spacing acute"))
	entityReferences.Set("micro", buildEntityReferenceDatum("00B5", " micro ", "micro sign"))
	entityReferences.Set("para", buildEntityReferenceDatum("00B6", " paragraph ", "pilcrow sign = paragraph sign"))
	entityReferences.Set("middot", buildEntityReferenceDatum("00B7", data.EmptyString, "middle dot = Georgian comma = Greek middle dot"))
	entityReferences.Set("cedil", buildEntityReferenceDatum("00B8", data.EmptyString, "cedilla = spacing cedilla"))
	entityReferences.Set("sup1", buildEntityReferenceDatum("00B9", data.EmptyString, "superscript one = superscript digit one"))
	entityReferences.Set("ordm", buildEntityReferenceDatum("00BA", data.EmptyString, "masculine ordinal indicator"))
	entityReferences.Set("raquo", buildEntityReferenceDatum("00BB", ", end quote, ", "right-pointing double angle quotation mark = right pointing guillemet"))
	entityReferences.Set("frac14", buildEntityReferenceDatum("00BC", " one quarter ", "vulgar fraction one quarter = fraction one quarter"))
	entityReferences.Set("frac12", buildEntityReferenceDatum("00BD", " one half ", "vulgar fraction one half = fraction one half"))
	entityReferences.Set("frac34", buildEntityReferenceDatum("00BE", " three quarters ", "vulgar fraction three quarters = fraction three quarters"))
	entityReferences.Set("iquest", buildEntityReferenceDatum("00BF", data.EmptyString, "inverted question mark = turned question mark"))
	entityReferences.Set("Agrave", buildEntityReferenceDatum("00C0", data.EmptyString, "latin capital letter A with grave = latin capital letter A grave"))
	entityReferences.Set("Aacute", buildEntityReferenceDatum("00C1", data.EmptyString, "latin capital letter A with acute"))
	entityReferences.Set("Acirc", buildEntityReferenceDatum("00C2", data.EmptyString, "latin capital letter A with circumflex"))
	entityReferences.Set("Atilde", buildEntityReferenceDatum("00C3", data.EmptyString, "latin capital letter A with tilde"))
	entityReferences.Set("Auml", buildEntityReferenceDatum("00C4", data.EmptyString, "latin capital letter A with diaeresis"))
	entityReferences.Set("Aring", buildEntityReferenceDatum("00C5", data.EmptyString, "latin capital letter A with ring above = latin capital letter A ring"))
	entityReferences.Set("AElig", buildEntityReferenceDatum("00C6", data.EmptyString, "latin capital letter AE = latin capital ligature AE"))
	entityReferences.Set("Ccedil", buildEntityReferenceDatum("00C7", data.EmptyString, "latin capital letter C with cedilla"))
	entityReferences.Set("Egrave", buildEntityReferenceDatum("00C8", data.EmptyString, "latin capital letter E with grave"))
	entityReferences.Set("Eacute", buildEntityReferenceDatum("00C9", data.EmptyString, "latin capital letter E with acute"))
	entityReferences.Set("Ecirc", buildEntityReferenceDatum("00CA", data.EmptyString, "latin capital letter E with circumflex"))
	entityReferences.Set("Euml", buildEntityReferenceDatum("00CB", data.EmptyString, "latin capital letter E with diaeresis"))
	entityReferences.Set("Igrave", buildEntityReferenceDatum("00CC", data.EmptyString, "latin capital letter I with grave"))
	entityReferences.Set("Iacute", buildEntityReferenceDatum("00CD", data.EmptyString, "latin capital letter I with acute"))
	entityReferences.Set("Icirc", buildEntityReferenceDatum("00CE", data.EmptyString, "latin capital letter I with circumflex"))
	entityReferences.Set("Iuml", buildEntityReferenceDatum("00CF", data.EmptyString, "latin capital letter I with diaeresis"))
	entityReferences.Set("ETH", buildEntityReferenceDatum("00D0", data.EmptyString, "latin capital letter ETH"))
	entityReferences.Set("Ntilde", buildEntityReferenceDatum("00D1", data.EmptyString, "latin capital letter N with tilde"))
	entityReferences.Set("Ograve", buildEntityReferenceDatum("00D2", data.EmptyString, "latin capital letter O with grave"))
	entityReferences.Set("Oacute", buildEntityReferenceDatum("00D3", data.EmptyString, "latin capital letter O with acute"))
	entityReferences.Set("Ocirc", buildEntityReferenceDatum("00D4", data.EmptyString, "latin capital letter O with circumflex"))
	entityReferences.Set("Otilde", buildEntityReferenceDatum("00D5", data.EmptyString, "latin capital letter O with tilde"))
	entityReferences.Set("Ouml", buildEntityReferenceDatum("00D6", data.EmptyString, "latin capital letter O with diaeresis"))
	entityReferences.Set("times", buildEntityReferenceDatum("00D7", " multiplied by ", "multiplication sign"))
	entityReferences.Set("Oslash", buildEntityReferenceDatum("00D8", data.EmptyString, "latin capital letter O with stroke = latin capital letter O slash"))
	entityReferences.Set("Ugrave", buildEntityReferenceDatum("00D9", data.EmptyString, "latin capital letter U with grave"))
	entityReferences.Set("Uacute", buildEntityReferenceDatum("00DA", data.EmptyString, "latin capital letter U with acute"))
	entityReferences.Set("Ucirc", buildEntityReferenceDatum("00DB", data.EmptyString, "latin capital letter U with circumflex"))
	entityReferences.Set("Uuml", buildEntityReferenceDatum("00DC", data.EmptyString, "latin capital letter U with diaeresis"))
	entityReferences.Set("Yacute", buildEntityReferenceDatum("00DD", data.EmptyString, "latin capital letter Y with acute"))
	entityReferences.Set("THORN", buildEntityReferenceDatum("00DE", data.EmptyString, "latin capital letter THORN"))
	entityReferences.Set("szlig", buildEntityReferenceDatum("00DF", data.EmptyString, "latin small letter sharp s = ess-zed"))
	entityReferences.Set("agrave", buildEntityReferenceDatum("00E0", data.EmptyString, "latin small letter a with grave = latin small letter a grave"))
	entityReferences.Set("aacute", buildEntityReferenceDatum("00E1", data.EmptyString, "latin small letter a with acute"))
	entityReferences.Set("acirc", buildEntityReferenceDatum("00E2", data.EmptyString, "latin small letter a with circumflex"))
	entityReferences.Set("atilde", buildEntityReferenceDatum("00E3", data.EmptyString, "latin small letter a with tilde"))
	entityReferences.Set("auml", buildEntityReferenceDatum("00E4", data.EmptyString, "latin small letter a with diaeresis"))
	entityReferences.Set("aring", buildEntityReferenceDatum("00E5", data.EmptyString, "latin small letter a with ring above = latin small letter a ring"))
	entityReferences.Set("aelig", buildEntityReferenceDatum("00E6", data.EmptyString, "latin small letter ae = latin small ligature ae"))
	entityReferences.Set("ccedil", buildEntityReferenceDatum("00E7", data.EmptyString, "latin small letter c with cedilla"))
	entityReferences.Set("egrave", buildEntityReferenceDatum("00E8", data.EmptyString, "latin small letter e with grave"))
	entityReferences.Set("eacute", buildEntityReferenceDatum("00E9", data.EmptyString, "latin small letter e with acute"))
	entityReferences.Set("ecirc", buildEntityReferenceDatum("00EA", data.EmptyString, "latin small letter e with circumflex"))
	entityReferences.Set("euml", buildEntityReferenceDatum("00EB", data.EmptyString, "latin small letter e with diaeresis"))
	entityReferences.Set("igrave", buildEntityReferenceDatum("00EC", data.EmptyString, "latin small letter i with grave"))
	entityReferences.Set("iacute", buildEntityReferenceDatum("00ED", data.EmptyString, "latin small letter i with acute"))
	entityReferences.Set("icirc", buildEntityReferenceDatum("00EE", data.EmptyString, "latin small letter i with circumflex"))
	entityReferences.Set("iuml", buildEntityReferenceDatum("00EF", data.EmptyString, "latin small letter i with diaeresis"))
	entityReferences.Set("eth", buildEntityReferenceDatum("00F0", data.EmptyString, "latin small letter eth"))
	entityReferences.Set("ntilde", buildEntityReferenceDatum("00F1", data.EmptyString, "latin small letter n with tilde"))
	entityReferences.Set("ograve", buildEntityReferenceDatum("00F2", data.EmptyString, "latin small letter o with grave"))
	entityReferences.Set("oacute", buildEntityReferenceDatum("00F3", data.EmptyString, "latin small letter o with acute"))
	entityReferences.Set("ocirc", buildEntityReferenceDatum("00F4", data.EmptyString, "latin small letter o with circumflex"))
	entityReferences.Set("otilde", buildEntityReferenceDatum("00F5", data.EmptyString, "latin small letter o with tilde"))
	entityReferences.Set("ouml", buildEntityReferenceDatum("00F6", data.EmptyString, "latin small letter o with diaeresis"))
	entityReferences.Set("divide", buildEntityReferenceDatum("00F7", " divided by ", "division sign"))
	entityReferences.Set("oslash", buildEntityReferenceDatum("00F8", data.EmptyString, "latin small letter o with stroke = latin small letter o slash"))
	entityReferences.Set("ugrave", buildEntityReferenceDatum("00F9", data.EmptyString, "latin small letter u with grave"))
	entityReferences.Set("uacute", buildEntityReferenceDatum("00FA", data.EmptyString, "latin small letter u with acute"))
	entityReferences.Set("ucirc", buildEntityReferenceDatum("00FB", data.EmptyString, "latin small letter u with circumflex"))
	entityReferences.Set("uuml", buildEntityReferenceDatum("00FC", data.EmptyString, "latin small letter u with diaeresis"))
	entityReferences.Set("yacute", buildEntityReferenceDatum("00FD", data.EmptyString, "latin small letter y with acute"))
	entityReferences.Set("thorn", buildEntityReferenceDatum("00FE", data.EmptyString, "latin small letter thorn"))
	entityReferences.Set("yuml", buildEntityReferenceDatum("00FF", data.EmptyString, "latin small letter y with diaeresis"))
	entityReferences.Set("fnof", buildEntityReferenceDatum("0192", data.EmptyString, "latin small f with hook = function = florin"))

	/* Greek */
	entityReferences.Set("Alpha", buildEntityReferenceDatum("0391", data.EmptyString, "greek capital letter alpha"))
	entityReferences.Set("Beta", buildEntityReferenceDatum("0392", data.EmptyString, "greek capital letter beta"))
	entityReferences.Set("Gamma", buildEntityReferenceDatum("0393", data.EmptyString, "greek capital letter gamma"))
	entityReferences.Set("Delta", buildEntityReferenceDatum("0394", data.EmptyString, "greek capital letter delta"))
	entityReferences.Set("Epsilon", buildEntityReferenceDatum("0395", data.EmptyString, "greek capital letter epsilon"))
	entityReferences.Set("Zeta", buildEntityReferenceDatum("0396", data.EmptyString, "greek capital letter zeta"))
	entityReferences.Set("Eta", buildEntityReferenceDatum("0397", data.EmptyString, "greek capital letter eta"))
	entityReferences.Set("Theta", buildEntityReferenceDatum("0398", data.EmptyString, "greek capital letter theta"))
	entityReferences.Set("Iota", buildEntityReferenceDatum("0399", data.EmptyString, "greek capital letter iota"))
	entityReferences.Set("Kappa", buildEntityReferenceDatum("039A", data.EmptyString, "greek capital letter kappa"))
	entityReferences.Set("Lambda", buildEntityReferenceDatum("039B", data.EmptyString, "greek capital letter lambda"))
	entityReferences.Set("Mu", buildEntityReferenceDatum("039C", data.EmptyString, "greek capital letter mu"))
	entityReferences.Set("Nu", buildEntityReferenceDatum("039D", data.EmptyString, "greek capital letter nu"))
	entityReferences.Set("Xi", buildEntityReferenceDatum("039E", data.EmptyString, "greek capital letter xi"))
	entityReferences.Set("Omicron", buildEntityReferenceDatum("039F", data.EmptyString, "greek capital letter omicron"))
	entityReferences.Set("Pi", buildEntityReferenceDatum("03A0", data.EmptyString, "greek capital letter pi"))
	entityReferences.Set("Rho", buildEntityReferenceDatum("03A1", data.EmptyString, "greek capital letter rho"))
	/* there is no Sigmaf, and no U+03A2 character either */
	entityReferences.Set("Sigma", buildEntityReferenceDatum("03A3", data.EmptyString, "greek capital letter sigma"))
	entityReferences.Set("Tau", buildEntityReferenceDatum("03A4", data.EmptyString, "greek capital letter tau"))
	entityReferences.Set("Upsilon", buildEntityReferenceDatum("03A5", data.EmptyString, "greek capital letter upsilon"))
	entityReferences.Set("Phi", buildEntityReferenceDatum("03A6", data.EmptyString, "greek capital letter phi"))
	entityReferences.Set("Chi", buildEntityReferenceDatum("03A7", data.EmptyString, "greek capital letter chi"))
	entityReferences.Set("Psi", buildEntityReferenceDatum("03A8", data.EmptyString, "greek capital letter psi"))
	entityReferences.Set("Omega", buildEntityReferenceDatum("03A9", data.EmptyString, "greek capital letter omega"))

	entityReferences.Set("alpha", buildEntityReferenceDatum("03B1", data.EmptyString, "greek small letter alpha"))
	entityReferences.Set("beta", buildEntityReferenceDatum("03B2", data.EmptyString, "greek small letter beta"))
	entityReferences.Set("gamma", buildEntityReferenceDatum("03B3", data.EmptyString, "greek small letter gamma"))
	entityReferences.Set("delta", buildEntityReferenceDatum("03B4", data.EmptyString, "greek small letter delta"))
	entityReferences.Set("epsilon", buildEntityReferenceDatum("03B5", data.EmptyString, "greek small letter epsilon"))
	entityReferences.Set("zeta", buildEntityReferenceDatum("03B6", data.EmptyString, "greek small letter zeta"))
	entityReferences.Set("eta", buildEntityReferenceDatum("03B7", data.EmptyString, "greek small letter eta"))
	entityReferences.Set("theta", buildEntityReferenceDatum("03B8", data.EmptyString, "greek small letter theta"))
	entityReferences.Set("iota", buildEntityReferenceDatum("03B9", data.EmptyString, "greek small letter iota"))
	entityReferences.Set("kappa", buildEntityReferenceDatum("03BA", data.EmptyString, "greek small letter kappa"))
	entityReferences.Set("lambda", buildEntityReferenceDatum("03BB", data.EmptyString, "greek small letter lambda"))
	entityReferences.Set("mu", buildEntityReferenceDatum("03BC", data.EmptyString, "greek small letter mu"))
	entityReferences.Set("nu", buildEntityReferenceDatum("03BD", data.EmptyString, "greek small letter nu"))
	entityReferences.Set("xi", buildEntityReferenceDatum("03BE", data.EmptyString, "greek small letter xi"))
	entityReferences.Set("omicron", buildEntityReferenceDatum("03BF", data.EmptyString, "greek small letter omicron"))
	entityReferences.Set("pi", buildEntityReferenceDatum("03C0", data.EmptyString, "greek small letter pi"))
	entityReferences.Set("rho", buildEntityReferenceDatum("03C1", data.EmptyString, "greek small letter rho"))
	entityReferences.Set("sigmaf", buildEntityReferenceDatum("03C2", data.EmptyString, "greek small letter final sigma"))
	entityReferences.Set("sigma", buildEntityReferenceDatum("03C3", data.EmptyString, "greek small letter sigma"))
	entityReferences.Set("tau", buildEntityReferenceDatum("03C4", data.EmptyString, "greek small letter tau"))
	entityReferences.Set("upsilon", buildEntityReferenceDatum("03C5", data.EmptyString, "greek small letter upsilon"))
	entityReferences.Set("phi", buildEntityReferenceDatum("03C6", data.EmptyString, "greek small letter phi"))
	entityReferences.Set("chi", buildEntityReferenceDatum("03C7", data.EmptyString, "greek small letter chi"))
	entityReferences.Set("psi", buildEntityReferenceDatum("03C8", data.EmptyString, "greek small letter psi"))
	entityReferences.Set("omega", buildEntityReferenceDatum("03C9", data.EmptyString, "greek small letter omega"))
	entityReferences.Set("thetasym", buildEntityReferenceDatum("03D1", data.EmptyString, "greek small letter theta symbol"))
	entityReferences.Set("upsih", buildEntityReferenceDatum("03D2", data.EmptyString, "greek upsilon with hook symbol"))
	entityReferences.Set("piv", buildEntityReferenceDatum("03D6", data.EmptyString, "greek pi symbol"))

	/* General Punctuation */
	entityReferences.Set("bull", buildEntityReferenceDatum("2022", " bullet point ", "bullet = black small circle"))
	/* bullet is NOT the same as bullet operator, U+2219 */
	entityReferences.Set("hellip", buildEntityReferenceDatum("2026", " ", "horizontal ellipsis = three dot leader"))
	entityReferences.Set("prime", buildEntityReferenceDatum("2032", data.EmptyString, "prime = minutes = feet"))
	entityReferences.Set("Prime", buildEntityReferenceDatum("2033", data.EmptyString, "double prime = seconds = inches"))
	entityReferences.Set("oline", buildEntityReferenceDatum("203E", data.EmptyString, "overline = spacing overscore"))
	entityReferences.Set("frasl", buildEntityReferenceDatum("2044", " divided by ", "fraction slash"))

	/* Letterlike Symbols */
	entityReferences.Set("weierp", buildEntityReferenceDatum("2118", " power set ", "script capital P = power set = Weierstrass p"))
	entityReferences.Set("image", buildEntityReferenceDatum("2111", " imaginary part ", "blackletter capital I = imaginary part"))
	entityReferences.Set("real", buildEntityReferenceDatum("211C", " real part ", "blackletter capital R = real part symbol"))
	entityReferences.Set("trade", buildEntityReferenceDatum("2122", " trade mark ", "trade mark sign"))
	entityReferences.Set("alefsym", buildEntityReferenceDatum("2135", " first transfinite cardinal ", "alef symbol = first transfinite cardinal"))

	/* Arrows */
	entityReferences.Set("larr", buildEntityReferenceDatum("2190", " arrow pointing left ", "leftwards arrow"))
	entityReferences.Set("uarr", buildEntityReferenceDatum("2191", " arrow pointing up ", "upwards arrow"))
	entityReferences.Set("rarr", buildEntityReferenceDatum("2192", " arrow pointing right ", "rightwards arrow"))
	entityReferences.Set("darr", buildEntityReferenceDatum("2193", " arrow pointing down ", "downwards arrow"))
	entityReferences.Set("harr", buildEntityReferenceDatum("2194", " arrow pointing left and right ", "left right arrow"))
	entityReferences.Set("crarr", buildEntityReferenceDatum("21B5", " arrow pointing down and left ", "downwards arrow with corner leftwards = carriage return"))
	entityReferences.Set("lArr", buildEntityReferenceDatum("21D0", " double arrow pointing left ", "leftwards double arrow"))
	entityReferences.Set("uArr", buildEntityReferenceDatum("21D1", " double arrow pointing up ", "upwards double arrow"))
	entityReferences.Set("rArr", buildEntityReferenceDatum("21D2", " double arrow pointing right ", "rightwards double arrow"))
	entityReferences.Set("dArr", buildEntityReferenceDatum("21D3", " double arrow pointing down ", "downwards double arrow"))
	entityReferences.Set("hArr", buildEntityReferenceDatum("21D4", " double arrow pointing left and right ", "left right double arrow"))

	/* Mathematical Operators */
	entityReferences.Set("forall", buildEntityReferenceDatum("2200", " for all ", "for all"))
	entityReferences.Set("part", buildEntityReferenceDatum("2202", " partial differential ", "partial differential"))
	entityReferences.Set("exist", buildEntityReferenceDatum("2203", " there exists ", "there exists"))
	entityReferences.Set("empty", buildEntityReferenceDatum("2205", " empty set ", "empty set = null set = diameter"))
	entityReferences.Set("nabla", buildEntityReferenceDatum("2207", " nabla ", "nabla = backward difference"))
	entityReferences.Set("isin", buildEntityReferenceDatum("2208", " element of ", "element of"))
	entityReferences.Set("notin", buildEntityReferenceDatum("2209", " not an element of ", "not an element of"))
	entityReferences.Set("ni", buildEntityReferenceDatum("220B", " contains as member ", "contains as member"))
	entityReferences.Set("prod", buildEntityReferenceDatum("220F", " enn airy product ", "n-ary product = product sign"))
	entityReferences.Set("sum", buildEntityReferenceDatum("2211", " enn airy sum", "n-ary sumation"))
	entityReferences.Set("minus", buildEntityReferenceDatum("2212", " minus ", "minus sign"))
	entityReferences.Set("lowast", buildEntityReferenceDatum("2217", " asterisk ", "asterisk operator"))
	entityReferences.Set("radic", buildEntityReferenceDatum("221A", " square root ", "square root = radical sign"))
	entityReferences.Set("prop", buildEntityReferenceDatum("221D", " proportional to ", "proportional to"))
	entityReferences.Set("infin", buildEntityReferenceDatum("221E", " infinity ", "infinity"))
	entityReferences.Set("ang", buildEntityReferenceDatum("2220", " angle ", "angle"))
	entityReferences.Set("and", buildEntityReferenceDatum("2227", " and ", "logical and = wedge"))
	entityReferences.Set("or", buildEntityReferenceDatum("2228", " or ", "logical or = vee"))
	entityReferences.Set("cap", buildEntityReferenceDatum("2229", " intersection ", "intersection = cap"))
	entityReferences.Set("cup", buildEntityReferenceDatum("222A", " union ", "union = cup"))
	entityReferences.Set("int", buildEntityReferenceDatum("222B", " integral ", "integral"))
	entityReferences.Set("there4", buildEntityReferenceDatum("2234", " therefore ", "therefore"))
	entityReferences.Set("sim", buildEntityReferenceDatum("223C", " similar to ", "tilde operator = varies with = similar to"))
	entityReferences.Set("cong", buildEntityReferenceDatum("2245", " approximately equal to ", "approximately equal to"))
	entityReferences.Set("asymp", buildEntityReferenceDatum("2248", " almost equal to ", "almost equal to = asymptotic to"))
	entityReferences.Set("ne", buildEntityReferenceDatum("2260", " not equal to ", "not equal to"))
	entityReferences.Set("equiv", buildEntityReferenceDatum("2261", " identical to ", "identical to"))
	entityReferences.Set("le", buildEntityReferenceDatum("2264", " less than or equal to ", "less-than or equal to"))
	entityReferences.Set("ge", buildEntityReferenceDatum("2265", " greater than or equal to ", "greater-than or equal to"))
	entityReferences.Set("sub", buildEntityReferenceDatum("2282", " subset of ", "subset of"))
	entityReferences.Set("sup", buildEntityReferenceDatum("2283", " superset of ", "superset of"))
	entityReferences.Set("nsub", buildEntityReferenceDatum("2284", " not a subset of ", "not a subset of"))
	entityReferences.Set("sube", buildEntityReferenceDatum("2286", " subset of or equal to ", "subset of or equal to"))
	entityReferences.Set("supe", buildEntityReferenceDatum("2287", " superset of or equal to ", "superset of or equal to"))
	entityReferences.Set("oplus", buildEntityReferenceDatum("2295", " direct sum ", "circled plus = direct sum"))
	entityReferences.Set("otimes", buildEntityReferenceDatum("2297", " vector product ", "circled times = vector product"))
	entityReferences.Set("perp", buildEntityReferenceDatum("22A5", " perpendicular ", "up tack = orthogonal to = perpendicular"))
	entityReferences.Set("sdot", buildEntityReferenceDatum("22C5", " dot ", "dot operator"))

	/* Miscellaneous Technical */
	entityReferences.Set("lceil", buildEntityReferenceDatum("2308", " left ceiling ", "left ceiling = apl upstile"))
	entityReferences.Set("rceil", buildEntityReferenceDatum("2309", " right ceiling ", "right ceiling"))
	entityReferences.Set("lfloor", buildEntityReferenceDatum("230A", " left floor ", "left floor = apl downstile"))
	entityReferences.Set("rfloor", buildEntityReferenceDatum("230B", " right floor ", "right floor"))
	entityReferences.Set("lang", buildEntityReferenceDatum("2329", " left angle bracket ", "left-pointing angle bracket = bra"))

	/* Geometric Shapes */
	entityReferences.Set("loz", buildEntityReferenceDatum("25CA", " lozenge ", "lozenge"))

	/* Miscellaneous Symbols */
	entityReferences.Set("spades", buildEntityReferenceDatum("2660", data.SpaceString, "black spade suit"))
	/* black here seems to mean filled as opposed to hollow */
	entityReferences.Set("clubs", buildEntityReferenceDatum("2663", data.SpaceString, "black club suit = shamrock"))
	entityReferences.Set("hearts", buildEntityReferenceDatum("2665", data.SpaceString, "black heart suit = valentine"))
	entityReferences.Set("diams", buildEntityReferenceDatum("2666", data.SpaceString, "black diamond suit"))

	/* C0 Controls and Basic Latin */
	/* Latin Extended-A */
	entityReferences.Set("OElig", buildEntityReferenceDatum("0152", data.EmptyString, "latin capital ligature OE"))
	entityReferences.Set("oelig", buildEntityReferenceDatum("0153", data.EmptyString, "latin small ligature oe"))
	/* ligature is a misnomer, this is a separate character in some languages */
	entityReferences.Set("Scaron", buildEntityReferenceDatum("0160", data.EmptyString, "latin capital letter S with caron"))
	entityReferences.Set("scaron", buildEntityReferenceDatum("0161", data.EmptyString, "latin small letter s with caron"))
	entityReferences.Set("Yuml", buildEntityReferenceDatum("0178", data.EmptyString, "latin capital letter Y with diaeresis"))

	/* Spacing Modifier Letters */
	entityReferences.Set("circ", buildEntityReferenceDatum("02C6", data.EmptyString, "modifier letter circumflex accent"))
	entityReferences.Set("tilde", buildEntityReferenceDatum("02DC", " tilde ", "small tilde"))

	/* General Punctuation */
	entityReferences.Set("ensp", buildEntityReferenceDatum("2002", data.SpaceString, "en space"))
	entityReferences.Set("emsp", buildEntityReferenceDatum("2003", data.SpaceString, "em space"))
	entityReferences.Set("thinsp", buildEntityReferenceDatum("2009", data.SpaceString, "thin space"))
	entityReferences.Set("zwnj", buildEntityReferenceDatum("200C", data.SpaceString, "zero width non-joiner"))
	entityReferences.Set("zwj", buildEntityReferenceDatum("200D", data.SpaceString, "zero width joiner"))
	entityReferences.Set("lrm", buildEntityReferenceDatum("200E", " left to right ", "left-to-right mark"))
	entityReferences.Set("rlm", buildEntityReferenceDatum("200F", " right to left ", "right-to-left mark"))
	entityReferences.Set("ndash", buildEntityReferenceDatum("2013", " - ", "en dash"))
	entityReferences.Set("mdash", buildEntityReferenceDatum("2014", " - ", "em dash"))
	entityReferences.Set("lsquo", buildEntityReferenceDatum("2018", " quote, ", "left single quotation mark"))
	entityReferences.Set("rsquo", buildEntityReferenceDatum("2019", ", end quote, ", "right single quotation mark"))
	entityReferences.Set("sbquo", buildEntityReferenceDatum("201A", " quote ", "single low-9 quotation mark"))
	entityReferences.Set("ldquo", buildEntityReferenceDatum("201C", " quote, ", "left double quotation mark"))
	entityReferences.Set("rdquo", buildEntityReferenceDatum("201D", ", end quote, ", "right double quotation mark"))
	entityReferences.Set("bdquo", buildEntityReferenceDatum("201E", " quote ", "double low-9 quotation mark"))
	entityReferences.Set("dagger", buildEntityReferenceDatum("2020", " dagger ", "dagger"))
	entityReferences.Set("Dagger", buildEntityReferenceDatum("2021", " double dagger ", "double dagger"))
	entityReferences.Set("permil", buildEntityReferenceDatum("2030", " per mille ", "per mille sign"))
	entityReferences.Set("lsaquo", buildEntityReferenceDatum("2039", " quote, ", "single left-pointing angle quotation mark"))
	/* lsaquo is proposed but not yet ISO standardized */
	entityReferences.Set("rsaquo", buildEntityReferenceDatum("203A", ", end quote, ", "single right-pointing angle quotation mark"))
	/* rsaquo is proposed but not yet ISO standardized */
	entityReferences.Set("euro", buildEntityReferenceDatum("20AC", " euros ", "euro sign"))
}

// transalates web site text to speakable text
func MakeSpeakable(vxml string) string {
    return entityReferences.makeSpeakable(vxml)
}

// Creates the entities list for the web page doctype tag by writing them to the bytes.Buffer.
func (this *entityReferenceData) BuildDocTypeInternalEntitryRefs(bfr *bytes.Buffer) {
	for k, v := range this.data {
		bfr.WriteString("  <!ENTITY ")
		bfr.WriteString(k)
		bfr.WriteString(" \"")
		bfr.WriteString(v.Speech)
		bfr.WriteString("\">\r\n")
	}
}

// Converts entity references to their speakable form.
// Use this with spoken text only
func (this *entityReferenceData) makeSpeakable(s string) string {
	var fixed string
	fixed = alphaEntityRegex.ReplaceAllStringFunc(s, this.makeAlphaSpeakable)
	fixed = decEntityRegex.ReplaceAllStringFunc(fixed, this.makeDecSpeakable)
    return fixed
}

func (this *entityReferenceData) makeAlphaSpeakable(s string) string {
	var key string
	var value entityReferenceDatum
	var ok bool
	key = strings.ToLower(s[1 : len(s)-1])
    value, ok = this.data[key]
	if ok {
		return value.Speech
	} else {
		return s
	}
}

func (this *entityReferenceData) makeDecSpeakable(s string) string {
	var strkey string
	var value entityReferenceDatum
	var ok bool
    var ikey int
    ikey, ok = data.StringToInt(s[1 : len(s)-1])
    if !ok {
        return s
    }
	strkey, ok = this.int2key[ikey]
    if !ok {
        return s
    }
    value, ok = this.data[strkey]
	if ok {
		return value.Speech
	} else {
		return s
	}
}
